package se.mbaeumer.jarvisualizer.core.services;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;

import se.mbaeumer.jarvisualizer.core.model.AbstractNode;
import se.mbaeumer.jarvisualizer.core.model.ClassNode;
import se.mbaeumer.jarvisualizer.core.model.EnumNode;
import se.mbaeumer.jarvisualizer.core.model.FileNode;
import se.mbaeumer.jarvisualizer.core.model.Graph;
import se.mbaeumer.jarvisualizer.core.model.ImageNode;
import se.mbaeumer.jarvisualizer.core.model.InterfaceNode;
import se.mbaeumer.jarvisualizer.core.model.PackageNode;
import se.mbaeumer.jarvisualizer.core.model.SimpleEdge;
import se.mbaeumer.jarvisualizer.jarreader.JarContentReader;
import se.mbaeumer.jarvisualizer.util.GraphUtil;
import javafx.concurrent.Service;
import javafx.concurrent.Task;

public class GraphCreationService extends Service {
	private Graph graph;
	private String filename;

	public GraphCreationService(String filename) {
		this.filename = filename;
		this.graph = new Graph();
	}

	@Override
	protected Task createTask() {
		return new Task(){
			@Override
			protected Object call() throws Exception {
				Enumeration<JarEntry> jarEntries = JarContentReader.read(filename);
				int nodeId = 0;
				int edgeId = 0;
				int level = 0;
				int entry = 0;
				ArrayList<JarEntry> entryList = java.util.Collections.list(jarEntries);
				jarEntries = JarContentReader.read(filename);
				while (jarEntries.hasMoreElements()){
					updateProgress(entry, entryList.size());
					entry++;
					JarEntry jarEntry = jarEntries.nextElement();
					String fullPath = jarEntry.toString().replace("/", ".");
					
					String[] nodes = jarEntry.toString().split("/");
					if (nodes[0].equalsIgnoreCase("meta-inf")){
						continue;
					}
					AbstractNode previousNode = null;
					// go through all nodes in the current string
					for (int i=0;i<nodes.length;i++){
						AbstractNode currentNode = null;
						level = i;
						
						if (nodes[i].endsWith(".class")){
							// check if it is an inner class
							if (nodes[i].contains("$")){
								continue;
							}
							
							//find out if it is a class/enum/interface
							Class c = null;
							try{
								c = detectClass(fullPath);
							}catch(ClassNotFoundException | MalformedURLException e){
								
							}finally{
								// TODO: Detect .class-file / undefined java-artifact
								if (c==null){
									continue;
								}
							}
							
							
							
							
							
							/*{URL url = new URL("jar:file:" + filename + "!/");
							URLClassLoader loader = new URLClassLoader(new URL[]{url});
							
							try {
								c = Class.forName(fullPath.replace(".class", ""), false, loader);
							} catch (ClassNotFoundException e) {
								e.printStackTrace();
							}
							*/
							/*if (c == null){
								currentNode = new ClassNode(nodeId, nodes[i], level, previousNode.getNodeId());
							}*/
							if (c.isInterface()){
								currentNode = new InterfaceNode(nodeId, nodes[i], level, previousNode.getNodeId());
							}else if (c.isEnum()){
								currentNode = new EnumNode(nodeId, nodes[i], level, previousNode.getNodeId());
							}else{
								currentNode = new ClassNode(nodeId, nodes[i], level, previousNode.getNodeId());
							}
							setPathFromRoot(currentNode, nodes, i);
						}
						else if ((nodes[i].endsWith(".jpg")) || (nodes[i].endsWith(".JPG")) || (nodes[i].endsWith(".png")) || (nodes[i].endsWith(".PNG")) ){
							if (previousNode != null){
								currentNode = new ImageNode(nodeId, nodes[i], level, previousNode.getNodeId());
								setPathFromRoot(currentNode, nodes, i);
							}
						}
						else if (nodes[i].contains(".")){
							if (previousNode != null){
								currentNode = new FileNode(nodeId, nodes[i], level, previousNode.getNodeId());
								setPathFromRoot(currentNode, nodes, i);
							}
						}
						else{
							currentNode = new PackageNode(nodeId, nodes[i],
									level, -1);
							setPathFromRoot(currentNode, nodes, i);
							if (currentNode.getNodeLevel()!=0){
								currentNode.setPreviousNode(previousNode.getNodeId());
							}
						}
						
						if (currentNode == null){
							continue;
						}
						
						if (graph.addNode(currentNode)){
							nodeId++;
							if (previousNode != null){
								SimpleEdge edge = new SimpleEdge(edgeId, previousNode.getNodeId(), currentNode.getNodeId());
								graph.addEdge(edge);
							}
							previousNode = currentNode;
						}else{
							previousNode = graph.findNode(currentNode);
						}
						
						edgeId++;
					}
				}
				
				graph = GraphUtil.createEdgeNumberVector(graph);
				return graph;
			}
		};
	}

	public Graph getGraph() {
		return graph;
	}

	public void setGraph(Graph graph) {
		this.graph = graph;
	}

	private void setPathFromRoot(AbstractNode node, String[] nodes,
			int currentIndex) {
		int i = 0;
		String path = "";
		while (i <= currentIndex) {
			path += nodes[i] + "/";
			i++;
		}

		if (path.contains(".class")) {
			path = path.replace(".class/", "");
		}
		node.setPathFromRoot(path);
	}
	
	@SuppressWarnings("rawtypes")
	private Class detectClass(String fullPath) throws MalformedURLException, ClassNotFoundException{
		Class c = null;
		
		URL url = new URL("jar:file:" + filename + "!/");
		URLClassLoader loader = new URLClassLoader(new URL[]{url});
		c = Class.forName(fullPath.replace(".class", ""), false, loader);
		
		return c;
	}

}
